package cn.fz.redisdemo01.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * 4. 自定义缓存管理
 * 以上缓存名称、过期时间和序列化方式都是默认设置，Spring 允许我们自定义缓存管理器。在大多数情况下，默认配置足够使用，但如果需要定制化的缓存行为，我们可以自定义缓存配置。通过实现 RedisCacheConfiguration，我们可以设置缓存的过期时间、序列化方式等。
 *
 * 4.1 RedisCacheConfiguration 类
 * Spring Boot 3 中，RedisCacheConfiguration 类是用于配置 Redis 缓存行为的核心组件之一。它提供了多种方法，
 * 用于自定义 Redis 缓存的各类设置，比如缓存过期时间、序列化策略等。以下是一些常用的配置选项：
 *
 * 5. Redis 缓存的常见问题和优化建议
 * 尽管 Redis 是一个高效的缓存解决方案，但在实际应用中，仍然有一些需要注意的问题：
 *
 * 缓存穿透：大量请求查询缓存中不存在的 key，导致所有请求都直接打到数据库上，降低系统性能。解决方案是使用布隆过滤器来拦截非法请求。
 * 缓存雪崩：当大量缓存同时过期时，可能会导致瞬间的大量请求直接涌入数据库，造成系统崩溃。可以通过设置不同的过期时间（TTL）来缓解这一问题。
 * 缓存击穿：某个热点 key 在缓存过期后，大量并发请求直接打到数据库上。解决方案是使用互斥锁，避免大量请求同时加载缓存。
 *
 * 原文链接：https://blog.csdn.net/u014390502/article/details/142713547
 */
// @Configuration
public class CacheConfig {
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(){
        // 4.1.7. 组合多种配置
        // 可以将多个配置组合到一起：
        return RedisCacheConfiguration.defaultCacheConfig()
            // 4.1.5. 使用前缀（Use Cache Key Prefixes）
            // Redis 中默认会为缓存键值加上一个命名空间的前缀，以防止不同缓存键冲突。可以自定义这个前缀，也可以关闭它：
            // .prefixCacheNameWith("myApp::") // 自定义缓存键前缀
            // .computePrefixWith(cacheName -> "customPrefix::" + cacheName + "::") // 使用自定义逻辑
            // 要禁用前缀：
            // .disableKeyPrefix() // 关闭缓存键前缀
            // 4.1.6. 设置空闲时间（Idle Time）
            // 你可以设置一个键的空闲时间，Redis 将会在指定的时间内删除不再被访问的键。
            // .entryTtl(Duration.ofMinutes(10))
            // .enableTimeToIdle()

            // 4.1.1. 过期时间（TTL - Time To Live）
            // 设置缓存条目的默认生存时间（TTL）。这决定了缓存数据在 Redis 中保留的时间。
            .entryTtl(Duration.ofMinutes(10))  // 设置缓存10分钟后过期
            // 4.1.4. 禁用缓存空值（Disable Caching Null Values）
            // 你可以配置不缓存空值，避免 Redis 存储 null，减少缓存的无效占用。
            .disableCachingNullValues()        // 不缓存 null 值
            // 4.1.2. 键序列化方式
            // 默认情况下，Redis 使用二进制存储键和值。RedisCacheConfiguration 提供了设置键（key）序列化方式的方法。常见的序列化方式包括 StringRedisSerializer，可以使用它来确保键以字符串格式存储：
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))  // 自定义 key 序列化
            // 4.1.3. 值序列化方式
            // 同样，值（value）的序列化方式也可以自定义。常用的序列化方式有 GenericJackson2JsonRedisSerializer，它将对象序列化为 JSON 格式：
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); // 自定义 value 序列化
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory, RedisCacheConfiguration redisCacheConfiguration){
        // RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5));
        // RedisCacheConfiguration longLivedConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
        //
        // Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        // cacheConfigurations.put("shortLivedCache", defaultConfig);
        // cacheConfigurations.put("longLivedCache", longLivedConfig);

        return RedisCacheManager.builder(redisConnectionFactory)
            .cacheDefaults(redisCacheConfiguration)
            .build();
    }
}
